#include <string.h>
#endif
-
typedef struct {
int id;
guint32 tag;
gint32 transient_for;
guint32 texture;
BroadwayNode *nodes;
+ GHashTable *node_lookup;
+};
+
+struct _BroadwayTexture {
+ grefcount refcount;
+ guint32 id;
+ GBytes *bytes;
};
static void broadway_server_resync_surfaces (BroadwayServer *server);
static void send_outstanding_roundtrips (BroadwayServer *server);
+static void broadway_server_ref_texture (BroadwayServer *server,
+ guint32 id);
+
static GType broadway_server_get_type (void);
G_DEFINE_TYPE (BroadwayServer, broadway_server, G_TYPE_OBJECT)
static void
-broadway_node_free (BroadwayNode *node)
+broadway_texture_free (BroadwayTexture *texture)
+{
+ g_bytes_unref (texture->bytes);
+ g_free (texture);
+}
+
+static void
+broadway_node_unref (BroadwayServer *server,
+ BroadwayNode *node)
{
int i;
- for (i = 0; i < node->n_children; i++)
- broadway_node_free (node->children[i]);
- g_free (node);
+ if (g_ref_count_dec (&node->refcount))
+ {
+ for (i = 0; i < node->n_children; i++)
+ broadway_node_unref (server, node->children[i]);
+
+ if (node->texture_id)
+ broadway_server_release_texture (server, node->texture_id);
+
+ g_free (node);
+ }
+}
+
+static BroadwayNode *
+broadway_node_ref (BroadwayNode *node)
+{
+ g_ref_count_inc (&node->refcount);
+
+ return node;
}
gboolean
server->surface_id_hash = g_hash_table_new (NULL, NULL);
server->id_counter = 0;
server->textures = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL,
- (GDestroyNotify)g_bytes_unref);
+ (GDestroyNotify)broadway_texture_free);
root = g_new0 (BroadwaySurface, 1);
root->id = server->id_counter++;
}
static void
-broadway_surface_free (BroadwaySurface *surface)
+broadway_surface_free (BroadwayServer *server,
+ BroadwaySurface *surface)
{
if (surface->nodes)
- broadway_node_free (surface->nodes);
+ broadway_node_unref (server, surface->nodes);
+ g_hash_table_unref (surface->node_lookup);
g_free (surface);
}
server->surfaces = g_list_remove (server->surfaces, surface);
g_hash_table_remove (server->surface_id_hash,
GINT_TO_POINTER (id));
- broadway_surface_free (surface);
+ broadway_surface_free (server, surface);
}
}
return server->output != NULL;
}
+#define NODE_SIZE_COLOR 1
+#define NODE_SIZE_FLOAT 1
+#define NODE_SIZE_POINT 2
+#define NODE_SIZE_SIZE 2
+#define NODE_SIZE_RECT (NODE_SIZE_POINT + NODE_SIZE_SIZE)
+#define NODE_SIZE_RRECT (NODE_SIZE_RECT + 4 * NODE_SIZE_SIZE)
+#define NODE_SIZE_COLOR_STOP (NODE_SIZE_FLOAT + NODE_SIZE_COLOR)
+#define NODE_SIZE_SHADOW (NODE_SIZE_COLOR + 3 * NODE_SIZE_FLOAT)
+
+static guint32
+rotl (guint32 value, int shift)
+{
+ if ((shift &= 32 - 1) == 0)
+ return value;
+ return (value << shift) | (value >> (32 - shift));
+}
+
+static BroadwayNode *
+decode_nodes (BroadwayServer *server,
+ BroadwaySurface *surface,
+ int len,
+ guint32 data[],
+ GHashTable *client_texture_map,
+ int *pos)
+{
+ BroadwayNode *node;
+ guint32 type, id;
+ guint32 i, n_stops, n_shadows, n_chars;
+ guint32 size, n_children;
+ gint32 texture_offset;
+ guint32 hash;
+
+ g_assert (*pos < len);
+
+ size = 0;
+ n_children = 0;
+ texture_offset = -1;
+
+ type = data[(*pos)++];
+ id = data[(*pos)++];
+ switch (type) {
+ case BROADWAY_NODE_REUSE:
+ node = g_hash_table_lookup (surface->node_lookup, GINT_TO_POINTER(id));
+ g_assert (node != NULL);
+ return broadway_node_ref (node);
+ break;
+ case BROADWAY_NODE_COLOR:
+ size = NODE_SIZE_RECT + NODE_SIZE_COLOR;
+ break;
+ case BROADWAY_NODE_BORDER:
+ size = NODE_SIZE_RRECT + 4 * NODE_SIZE_FLOAT + 4 * NODE_SIZE_COLOR;
+ break;
+ case BROADWAY_NODE_INSET_SHADOW:
+ case BROADWAY_NODE_OUTSET_SHADOW:
+ size = NODE_SIZE_RRECT + NODE_SIZE_COLOR + 4 * NODE_SIZE_FLOAT;
+ break;
+ case BROADWAY_NODE_TEXTURE:
+ texture_offset = 4;
+ size = 5;
+ break;
+ case BROADWAY_NODE_CONTAINER:
+ size = 1;
+ n_children = data[*pos];
+ break;
+ case BROADWAY_NODE_ROUNDED_CLIP:
+ size = NODE_SIZE_RRECT;
+ n_children = 1;
+ break;
+ case BROADWAY_NODE_CLIP:
+ size = NODE_SIZE_RECT;
+ n_children = 1;
+ break;
+ case BROADWAY_NODE_TRANSLATE:
+ size = NODE_SIZE_POINT;
+ n_children = 1;
+ break;
+ case BROADWAY_NODE_LINEAR_GRADIENT:
+ size = NODE_SIZE_RECT + 2 * NODE_SIZE_POINT;
+ n_stops = data[*pos + size++];
+ size += n_stops * NODE_SIZE_COLOR_STOP;
+ break;
+ case BROADWAY_NODE_SHADOW:
+ size = 1;
+ n_shadows = data[*pos];
+ size += n_shadows * NODE_SIZE_SHADOW;
+ n_children = 1;
+ break;
+ case BROADWAY_NODE_OPACITY:
+ size = NODE_SIZE_FLOAT;
+ n_children = 1;
+ break;
+ case BROADWAY_NODE_DEBUG:
+ n_chars = data[*pos];
+ size = 1 + (n_chars + 3) / 4;
+ n_children = 1;
+ break;
+ default:
+ g_assert_not_reached ();
+ }
+
+ node = g_malloc (sizeof(BroadwayNode) + (size - 1) * sizeof(guint32) + n_children * sizeof (BroadwayNode *));
+ g_ref_count_init (&node->refcount);
+ node->type = type;
+ node->id = id;
+ node->texture_id = 0;
+ node->n_children = n_children;
+ node->children = (BroadwayNode **)((char *)node + sizeof(BroadwayNode) + (size - 1) * sizeof(guint32));
+ node->n_data = size;
+ for (i = 0; i < size; i++)
+ {
+ node->data[i] = data[(*pos)++];
+ if (i == texture_offset)
+ {
+ node->texture_id = GPOINTER_TO_INT (g_hash_table_lookup (client_texture_map, GINT_TO_POINTER (node->data[i])));
+ broadway_server_ref_texture (server, node->texture_id);
+ node->data[i] = node->texture_id;
+ }
+ }
+
+ for (i = 0; i < n_children; i++)
+ node->children[i] = decode_nodes (server, surface, len, data, client_texture_map, pos);
+
+ hash = node->type << 16;
+
+ for (i = 0; i < size; i++)
+ hash ^= rotl (node->data[i], i);
+
+ for (i = 0; i < n_children; i++)
+ hash ^= rotl (node->children[i]->hash, i);
+
+ node->hash = hash;
+
+ return node;
+}
+
+static void
+init_node_lookup (BroadwaySurface *surface,
+ BroadwayNode *node)
+{
+ int i;
+
+ g_hash_table_insert (surface->node_lookup, GINT_TO_POINTER(node->id), node);
+ for (i = 0; i < node->n_children; i++)
+ init_node_lookup (surface, node->children[i]);
+}
+
/* passes ownership of nodes */
void
-broadway_server_surface_set_nodes (BroadwayServer *server,
- gint id,
- BroadwayNode *root)
+broadway_server_surface_update_nodes (BroadwayServer *server,
+ gint id,
+ guint32 data[],
+ int len,
+ GHashTable *client_texture_map)
{
BroadwaySurface *surface;
+ int pos = 0;
+ BroadwayNode *root;
surface = broadway_server_lookup_surface (server, id);
if (surface == NULL)
return;
+ root = decode_nodes (server, surface, len, data, client_texture_map, &pos);
+
if (server->output != NULL)
broadway_output_surface_set_nodes (server->output, surface->id,
root,
surface->nodes);
if (surface->nodes)
- broadway_node_free (surface->nodes);
+ broadway_node_unref (server, surface->nodes);
+
surface->nodes = root;
+
+ g_hash_table_remove_all (surface->node_lookup);
+
+ init_node_lookup (surface, surface->nodes);
}
guint32
broadway_server_upload_texture (BroadwayServer *server,
- GBytes *texture)
+ GBytes *bytes)
{
- guint32 id;
+ BroadwayTexture *texture;
+
+ texture = g_new0 (BroadwayTexture, 1);
+ g_ref_count_init (&texture->refcount);
+ texture->id = ++server->next_texture_id;
+ texture->bytes = g_bytes_ref (bytes);
- id = ++server->next_texture_id;
g_hash_table_replace (server->textures,
- GINT_TO_POINTER (id),
- g_bytes_ref (texture));
+ GINT_TO_POINTER (texture->id),
+ texture);
if (server->output)
- broadway_output_upload_texture (server->output, id, texture);
+ broadway_output_upload_texture (server->output, texture->id, texture->bytes);
+
+ return texture->id;
+}
+
+static void
+broadway_server_ref_texture (BroadwayServer *server,
+ guint32 id)
+{
+ BroadwayTexture *texture;
- return id;
+ texture = g_hash_table_lookup (server->textures, GINT_TO_POINTER (id));
+ if (texture)
+ g_ref_count_inc (&texture->refcount);
}
void
broadway_server_release_texture (BroadwayServer *server,
guint32 id)
{
- g_hash_table_remove (server->textures, GINT_TO_POINTER (id));
+ BroadwayTexture *texture;
- if (server->output)
- broadway_output_release_texture (server->output, id);
+ texture = g_hash_table_lookup (server->textures, GINT_TO_POINTER (id));
+
+ if (texture && g_ref_count_dec (&texture->refcount))
+ {
+ g_hash_table_remove (server->textures, GINT_TO_POINTER (id));
+
+ if (server->output)
+ broadway_output_release_texture (server->output, id);
+ }
}
gboolean
surface->width = width;
surface->height = height;
surface->is_temp = is_temp;
+ surface->node_lookup = g_hash_table_new (g_direct_hash, g_direct_equal);
g_hash_table_insert (server->surface_id_hash,
GINT_TO_POINTER (surface->id),
/* First upload all textures */
g_hash_table_iter_init (&iter, server->textures);
while (g_hash_table_iter_next (&iter, &key, &value))
- broadway_output_upload_texture (server->output,
- GPOINTER_TO_INT (key),
- (GBytes *)value);
+ {
+ BroadwayTexture *texture = value;
+ broadway_output_upload_texture (server->output,
+ GPOINTER_TO_INT (key),
+ texture->bytes);
+ }
/* Then create all surfaces */
for (l = server->surfaces; l != NULL; l = l->next)
{
GskRenderer parent_instance;
GdkBroadwayDrawContext *draw_context;
- GHashTable *fallback_cache;
- guint32 frame_nr;
+ guint32 next_node_id;
+
+ /* Set during rendering */
+ GArray *nodes; /* Owned by draw_contex */
+ GPtrArray *node_textures; /* Owned by draw_contex */
+ GHashTable *node_lookup;
+
+ /* Kept from last frame */
+ GHashTable *last_node_lookup;
+ GskRenderNode *last_root; /* Owning refs to the things in last_node_lookup */
};
struct _GskBroadwayRendererClass
add_uint32 (nodes, v);
}
-typedef struct {
- GskRenderNode *node;
- GdkTexture *texture;
- float off_x;
- float off_y;
- int used_in_frame;
-} FallbackCacheElement;
+static void
+collect_reused_child_nodes (GskRenderer *renderer,
+ GskRenderNode *node);
static void
-fallback_cache_element_free (FallbackCacheElement *element)
+collect_reused_node (GskRenderer *renderer,
+ GskRenderNode *node)
{
- gsk_render_node_unref (element->node);
- g_object_unref (element->texture);
- g_free (element);
+ GskBroadwayRenderer *self = GSK_BROADWAY_RENDERER (renderer);
+ guint32 old_id;
+
+ if (self->last_node_lookup &&
+ (old_id = GPOINTER_TO_INT(g_hash_table_lookup (self->last_node_lookup, node))) != 0)
+ {
+ g_hash_table_insert (self->node_lookup, node, GINT_TO_POINTER (old_id));
+ collect_reused_child_nodes (renderer, node);
+ }
}
-static GdkTexture *
-node_texture_fallback (GskRenderNode *node,
- float *off_x,
- float *off_y)
+
+static void
+collect_reused_child_nodes (GskRenderer *renderer,
+ GskRenderNode *node)
{
- cairo_surface_t *surface;
- cairo_t *cr;
- int x = floorf (node->bounds.origin.x);
- int y = floorf (node->bounds.origin.y);
- int width = ceil (node->bounds.origin.x + node->bounds.size.width) - x;
- int height = ceil (node->bounds.origin.y + node->bounds.size.height) - y;
- GdkTexture *texture;
+ guint i;
- surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height);
- cr = cairo_create (surface);
- cairo_translate (cr, -x, -y);
- gsk_render_node_draw (node, cr);
- cairo_destroy (cr);
+ switch (gsk_render_node_get_node_type (node))
+ {
+ case GSK_NOT_A_RENDER_NODE:
+ g_assert_not_reached ();
+ return;
- texture = gdk_texture_new_for_surface (surface);
- *off_x = x - node->bounds.origin.x;
- *off_y = y - node->bounds.origin.y;
+ /* Leaf nodes */
- return texture;
+ case GSK_TEXTURE_NODE:
+ case GSK_CAIRO_NODE:
+ case GSK_COLOR_NODE:
+ case GSK_BORDER_NODE:
+ case GSK_OUTSET_SHADOW_NODE:
+ case GSK_INSET_SHADOW_NODE:
+ case GSK_LINEAR_GRADIENT_NODE:
+
+ /* Fallbacks (=> leaf for now */
+ case GSK_COLOR_MATRIX_NODE:
+ case GSK_TEXT_NODE:
+ case GSK_REPEATING_LINEAR_GRADIENT_NODE:
+ case GSK_REPEAT_NODE:
+ case GSK_BLEND_NODE:
+ case GSK_CROSS_FADE_NODE:
+ case GSK_BLUR_NODE:
+
+ default:
+
+ break;
+
+ /* Bin nodes */
+
+ case GSK_SHADOW_NODE:
+ collect_reused_node (renderer,
+ gsk_shadow_node_get_child (node));
+ break;
+
+ case GSK_OPACITY_NODE:
+ collect_reused_node (renderer,
+ gsk_opacity_node_get_child (node));
+ break;
+
+ case GSK_ROUNDED_CLIP_NODE:
+ collect_reused_node (renderer,
+ gsk_rounded_clip_node_get_child (node));
+ break;
+
+ case GSK_CLIP_NODE:
+ collect_reused_node (renderer,
+ gsk_clip_node_get_child (node));
+ break;
+
+ case GSK_TRANSFORM_NODE:
+ collect_reused_node (renderer,
+ gsk_transform_node_get_child (node));
+ break;
+
+ case GSK_DEBUG_NODE:
+ collect_reused_node (renderer,
+ gsk_debug_node_get_child (node));
+ break;
+
+ /* Generic nodes */
+
+ case GSK_CONTAINER_NODE:
+ for (i = 0; i < gsk_container_node_get_n_children (node); i++)
+ collect_reused_node (renderer,
+ gsk_container_node_get_child (node, i));
+ break;
+
+ break; /* Fallback */
+ }
+}
+
+static gboolean
+add_new_node (GskRenderer *renderer,
+ GskRenderNode *node,
+ BroadwayNodeType type)
+{
+ GskBroadwayRenderer *self = GSK_BROADWAY_RENDERER (renderer);
+ guint32 id, old_id;
+
+ if (self->last_node_lookup &&
+ (old_id = GPOINTER_TO_INT (g_hash_table_lookup (self->last_node_lookup, node))) != 0)
+ {
+ add_uint32 (self->nodes, BROADWAY_NODE_REUSE);
+ add_uint32 (self->nodes, old_id);
+
+ g_hash_table_insert (self->node_lookup, node, GINT_TO_POINTER(old_id));
+ collect_reused_child_nodes (renderer, node);
+
+ return FALSE;
+ }
+
+ id = ++self->next_node_id;
+ g_hash_table_insert (self->node_lookup, node, GINT_TO_POINTER(id));
+
+ add_uint32 (self->nodes, type);
+ add_uint32 (self->nodes, id);
+
+ return TRUE;
}
/* Note: This tracks the offset so that we can convert
which is good for re-using subtrees. */
static void
gsk_broadway_renderer_add_node (GskRenderer *renderer,
- GArray *nodes,
- GPtrArray *node_textures,
GskRenderNode *node,
float offset_x,
float offset_y)
{
GdkDisplay *display = gdk_surface_get_display (gsk_renderer_get_surface (renderer));
GskBroadwayRenderer *self = GSK_BROADWAY_RENDERER (renderer);
+ GArray *nodes = self->nodes;
switch (gsk_render_node_get_node_type (node))
{
/* Leaf nodes */
case GSK_TEXTURE_NODE:
- {
- GdkTexture *texture = gsk_texture_node_get_texture (node);
- guint32 texture_id;
+ if (add_new_node (renderer, node, BROADWAY_NODE_TEXTURE))
+ {
+ GdkTexture *texture = gsk_texture_node_get_texture (node);
+ guint32 texture_id;
- g_ptr_array_add (node_textures, g_object_ref (texture)); /* Transfers ownership to node_textures */
- texture_id = gdk_broadway_display_ensure_texture (display, texture);
+ /* No need to add to self->node_textures here, the node will keep it alive until end of frame. */
- add_uint32 (nodes, BROADWAY_NODE_TEXTURE);
- add_rect (nodes, &node->bounds, offset_x, offset_y);
- add_uint32 (nodes, texture_id);
- }
+ texture_id = gdk_broadway_display_ensure_texture (display, texture);
+
+ add_rect (nodes, &node->bounds, offset_x, offset_y);
+ add_uint32 (nodes, texture_id);
+ }
return;
case GSK_CAIRO_NODE:
- {
- cairo_surface_t *surface = (cairo_surface_t *)gsk_cairo_node_peek_surface (node);
- cairo_surface_t *image_surface = NULL;
- GdkTexture *texture;
- guint32 texture_id;
-
- if (cairo_surface_get_type (surface) == CAIRO_SURFACE_TYPE_IMAGE)
- image_surface = cairo_surface_reference (surface);
- else
- {
- cairo_t *cr;
- image_surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
- ceilf (node->bounds.size.width),
- ceilf (node->bounds.size.height));
- cr = cairo_create (image_surface);
- cairo_set_source_surface (cr, surface, 0, 0);
- cairo_rectangle (cr, 0, 0, node->bounds.size.width, node->bounds.size.height);
- cairo_fill (cr);
- cairo_destroy (cr);
- }
-
- texture = gdk_texture_new_for_surface (image_surface);
- g_ptr_array_add (node_textures, g_object_ref (texture)); /* Transfers ownership to node_textures */
- texture_id = gdk_broadway_display_ensure_texture (display, texture);
-
- add_uint32 (nodes, BROADWAY_NODE_TEXTURE);
- add_rect (nodes, &node->bounds, offset_x, offset_y);
- add_uint32 (nodes, texture_id);
-
- cairo_surface_destroy (image_surface);
- }
+ if (add_new_node (renderer, node, BROADWAY_NODE_TEXTURE))
+ {
+ cairo_surface_t *surface = (cairo_surface_t *)gsk_cairo_node_peek_surface (node);
+ cairo_surface_t *image_surface = NULL;
+ GdkTexture *texture;
+ guint32 texture_id;
+
+ if (cairo_surface_get_type (surface) == CAIRO_SURFACE_TYPE_IMAGE)
+ image_surface = cairo_surface_reference (surface);
+ else
+ {
+ cairo_t *cr;
+ image_surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
+ ceilf (node->bounds.size.width),
+ ceilf (node->bounds.size.height));
+ cr = cairo_create (image_surface);
+ cairo_set_source_surface (cr, surface, 0, 0);
+ cairo_rectangle (cr, 0, 0, node->bounds.size.width, node->bounds.size.height);
+ cairo_fill (cr);
+ cairo_destroy (cr);
+ }
+
+ texture = gdk_texture_new_for_surface (image_surface);
+ g_ptr_array_add (self->node_textures, g_object_ref (texture)); /* Transfers ownership to node_textures */
+ texture_id = gdk_broadway_display_ensure_texture (display, texture);
+
+ add_rect (nodes, &node->bounds, offset_x, offset_y);
+ add_uint32 (nodes, texture_id);
+
+ cairo_surface_destroy (image_surface);
+ }
return;
case GSK_COLOR_NODE:
- {
- add_uint32 (nodes, BROADWAY_NODE_COLOR);
- add_rect (nodes, &node->bounds, offset_x, offset_y);
- add_rgba (nodes, gsk_color_node_peek_color (node));
- }
+ if (add_new_node (renderer, node, BROADWAY_NODE_COLOR))
+ {
+ add_rect (nodes, &node->bounds, offset_x, offset_y);
+ add_rgba (nodes, gsk_color_node_peek_color (node));
+ }
return;
case GSK_BORDER_NODE:
- {
- int i;
- add_uint32 (nodes, BROADWAY_NODE_BORDER);
- add_rounded_rect (nodes, gsk_border_node_peek_outline (node), offset_x, offset_y);
- for (i = 0; i < 4; i++)
- add_float (nodes, gsk_border_node_peek_widths (node)[i]);
- for (i = 0; i < 4; i++)
- add_rgba (nodes, &gsk_border_node_peek_colors (node)[i]);
- }
+ if (add_new_node (renderer, node, BROADWAY_NODE_BORDER))
+ {
+ int i;
+ add_rounded_rect (nodes, gsk_border_node_peek_outline (node), offset_x, offset_y);
+ for (i = 0; i < 4; i++)
+ add_float (nodes, gsk_border_node_peek_widths (node)[i]);
+ for (i = 0; i < 4; i++)
+ add_rgba (nodes, &gsk_border_node_peek_colors (node)[i]);
+ }
return;
case GSK_OUTSET_SHADOW_NODE:
- {
- add_uint32 (nodes, BROADWAY_NODE_OUTSET_SHADOW);
- add_rounded_rect (nodes, gsk_outset_shadow_node_peek_outline (node), offset_x, offset_y);
- add_rgba (nodes, gsk_outset_shadow_node_peek_color (node));
- add_float (nodes, gsk_outset_shadow_node_get_dx (node));
- add_float (nodes, gsk_outset_shadow_node_get_dy (node));
- add_float (nodes, gsk_outset_shadow_node_get_spread (node));
- add_float (nodes, gsk_outset_shadow_node_get_blur_radius (node));
- }
+ if (add_new_node (renderer, node, BROADWAY_NODE_OUTSET_SHADOW))
+ {
+ add_rounded_rect (nodes, gsk_outset_shadow_node_peek_outline (node), offset_x, offset_y);
+ add_rgba (nodes, gsk_outset_shadow_node_peek_color (node));
+ add_float (nodes, gsk_outset_shadow_node_get_dx (node));
+ add_float (nodes, gsk_outset_shadow_node_get_dy (node));
+ add_float (nodes, gsk_outset_shadow_node_get_spread (node));
+ add_float (nodes, gsk_outset_shadow_node_get_blur_radius (node));
+ }
return;
case GSK_INSET_SHADOW_NODE:
- {
- add_uint32 (nodes, BROADWAY_NODE_INSET_SHADOW);
- add_rounded_rect (nodes, gsk_inset_shadow_node_peek_outline (node), offset_x, offset_y);
- add_rgba (nodes, gsk_inset_shadow_node_peek_color (node));
- add_float (nodes, gsk_inset_shadow_node_get_dx (node));
- add_float (nodes, gsk_inset_shadow_node_get_dy (node));
- add_float (nodes, gsk_inset_shadow_node_get_spread (node));
- add_float (nodes, gsk_inset_shadow_node_get_blur_radius (node));
- }
+ if (add_new_node (renderer, node, BROADWAY_NODE_INSET_SHADOW))
+ {
+ add_rounded_rect (nodes, gsk_inset_shadow_node_peek_outline (node), offset_x, offset_y);
+ add_rgba (nodes, gsk_inset_shadow_node_peek_color (node));
+ add_float (nodes, gsk_inset_shadow_node_get_dx (node));
+ add_float (nodes, gsk_inset_shadow_node_get_dy (node));
+ add_float (nodes, gsk_inset_shadow_node_get_spread (node));
+ add_float (nodes, gsk_inset_shadow_node_get_blur_radius (node));
+ }
return;
case GSK_LINEAR_GRADIENT_NODE:
- {
- guint i, n;
-
- add_uint32 (nodes, BROADWAY_NODE_LINEAR_GRADIENT);
- add_rect (nodes, &node->bounds, offset_x, offset_y);
- add_point (nodes, gsk_linear_gradient_node_peek_start (node), offset_x, offset_y);
- add_point (nodes, gsk_linear_gradient_node_peek_end (node), offset_x, offset_y);
- n = gsk_linear_gradient_node_get_n_color_stops (node);
- add_uint32 (nodes, n);
- for (i = 0; i < n; i++)
- add_color_stop (nodes, &gsk_linear_gradient_node_peek_color_stops (node)[i]);
- }
+ if (add_new_node (renderer, node, BROADWAY_NODE_LINEAR_GRADIENT))
+ {
+ guint i, n;
+
+ add_rect (nodes, &node->bounds, offset_x, offset_y);
+ add_point (nodes, gsk_linear_gradient_node_peek_start (node), offset_x, offset_y);
+ add_point (nodes, gsk_linear_gradient_node_peek_end (node), offset_x, offset_y);
+ n = gsk_linear_gradient_node_get_n_color_stops (node);
+ add_uint32 (nodes, n);
+ for (i = 0; i < n; i++)
+ add_color_stop (nodes, &gsk_linear_gradient_node_peek_color_stops (node)[i]);
+ }
return;
/* Bin nodes */
case GSK_SHADOW_NODE:
- {
- gsize i, n_shadows = gsk_shadow_node_get_n_shadows (node);
- add_uint32 (nodes, BROADWAY_NODE_SHADOW);
- add_uint32 (nodes, n_shadows);
- for (i = 0; i < n_shadows; i++)
- {
- const GskShadow *shadow = gsk_shadow_node_peek_shadow (node, i);
- add_rgba (nodes, &shadow->color);
- add_float (nodes, shadow->dx);
- add_float (nodes, shadow->dy);
- add_float (nodes, shadow->radius);
- }
- gsk_broadway_renderer_add_node (renderer, nodes, node_textures,
- gsk_shadow_node_get_child (node),
- offset_x, offset_y);
- }
+ if (add_new_node (renderer, node, BROADWAY_NODE_SHADOW))
+ {
+ gsize i, n_shadows = gsk_shadow_node_get_n_shadows (node);
+
+ add_uint32 (nodes, n_shadows);
+ for (i = 0; i < n_shadows; i++)
+ {
+ const GskShadow *shadow = gsk_shadow_node_peek_shadow (node, i);
+ add_rgba (nodes, &shadow->color);
+ add_float (nodes, shadow->dx);
+ add_float (nodes, shadow->dy);
+ add_float (nodes, shadow->radius);
+ }
+ gsk_broadway_renderer_add_node (renderer,
+ gsk_shadow_node_get_child (node),
+ offset_x, offset_y);
+ }
return;
case GSK_OPACITY_NODE:
- {
- add_uint32 (nodes, BROADWAY_NODE_OPACITY);
- add_float (nodes, gsk_opacity_node_get_opacity (node));
- gsk_broadway_renderer_add_node (renderer, nodes, node_textures,
- gsk_opacity_node_get_child (node),
- offset_x, offset_y);
- }
+ if (add_new_node (renderer, node, BROADWAY_NODE_OPACITY))
+ {
+ add_float (nodes, gsk_opacity_node_get_opacity (node));
+ gsk_broadway_renderer_add_node (renderer,
+ gsk_opacity_node_get_child (node),
+ offset_x, offset_y);
+ }
return;
case GSK_ROUNDED_CLIP_NODE:
- {
- const GskRoundedRect *rclip = gsk_rounded_clip_node_peek_clip (node);
- add_uint32 (nodes, BROADWAY_NODE_ROUNDED_CLIP);
- add_rounded_rect (nodes, rclip, offset_x, offset_y);
- gsk_broadway_renderer_add_node (renderer, nodes, node_textures,
- gsk_rounded_clip_node_get_child (node),
- rclip->bounds.origin.x,
- rclip->bounds.origin.y);
- }
+ if (add_new_node (renderer, node, BROADWAY_NODE_ROUNDED_CLIP))
+ {
+ const GskRoundedRect *rclip = gsk_rounded_clip_node_peek_clip (node);
+
+ add_rounded_rect (nodes, rclip, offset_x, offset_y);
+ gsk_broadway_renderer_add_node (renderer,
+ gsk_rounded_clip_node_get_child (node),
+ rclip->bounds.origin.x,
+ rclip->bounds.origin.y);
+ }
return;
case GSK_CLIP_NODE:
- {
- const graphene_rect_t *clip = gsk_clip_node_peek_clip (node);
- add_uint32 (nodes, BROADWAY_NODE_CLIP);
- add_rect (nodes, clip, offset_x, offset_y);
- gsk_broadway_renderer_add_node (renderer, nodes, node_textures,
- gsk_clip_node_get_child (node),
- clip->origin.x,
- clip->origin.y);
- }
+ if (add_new_node (renderer, node, BROADWAY_NODE_CLIP))
+ {
+ const graphene_rect_t *clip = gsk_clip_node_peek_clip (node);
+
+ add_rect (nodes, clip, offset_x, offset_y);
+ gsk_broadway_renderer_add_node (renderer,
+ gsk_clip_node_get_child (node),
+ clip->origin.x,
+ clip->origin.y);
+ }
return;
case GSK_TRANSFORM_NODE:
{
GskTransform *transform = gsk_transform_node_get_transform (node);
GskTransformCategory category = gsk_transform_get_category (transform);
- float dx, dy;
if (category >= GSK_TRANSFORM_CATEGORY_2D_TRANSLATE)
{
- gsk_transform_to_translate (transform, &dx, &dy);
- add_uint32 (nodes, BROADWAY_NODE_TRANSLATE);
- add_xy (nodes, dx, dy, offset_x, offset_y);
- gsk_broadway_renderer_add_node (renderer, nodes, node_textures,
- gsk_transform_node_get_child (node),
- 0, 0);
+ if (add_new_node (renderer, node, BROADWAY_NODE_TRANSLATE)) {
+ float dx, dy;
+ gsk_transform_to_translate (transform, &dx, &dy);
+
+ add_xy (nodes, dx, dy, offset_x, offset_y);
+ gsk_broadway_renderer_add_node (renderer,
+ gsk_transform_node_get_child (node),
+ 0, 0);
+ }
}
else
{
/* Fallback to texture for now */
break;
}
-
}
return;
case GSK_DEBUG_NODE:
- {
- const char *message = gsk_debug_node_get_message (node);
- add_uint32 (nodes, BROADWAY_NODE_DEBUG);
- add_string (nodes, message);
- gsk_broadway_renderer_add_node (renderer, nodes, node_textures,
- gsk_debug_node_get_child (node), offset_x, offset_y);
- }
+ if (add_new_node (renderer, node, BROADWAY_NODE_DEBUG))
+ {
+ const char *message = gsk_debug_node_get_message (node);
+ add_string (nodes, message);
+ gsk_broadway_renderer_add_node (renderer,
+ gsk_debug_node_get_child (node), offset_x, offset_y);
+ }
return;
/* Generic nodes */
case GSK_CONTAINER_NODE:
- {
- guint i;
-
- add_uint32 (nodes, BROADWAY_NODE_CONTAINER);
- add_uint32 (nodes, gsk_container_node_get_n_children (node));
+ if (add_new_node (renderer, node, BROADWAY_NODE_CONTAINER))
+ {
+ guint i;
- for (i = 0; i < gsk_container_node_get_n_children (node); i++)
- gsk_broadway_renderer_add_node (renderer, nodes, node_textures,
- gsk_container_node_get_child (node, i), offset_x, offset_y);
- }
+ add_uint32 (nodes, gsk_container_node_get_n_children (node));
+ for (i = 0; i < gsk_container_node_get_n_children (node); i++)
+ gsk_broadway_renderer_add_node (renderer,
+ gsk_container_node_get_child (node, i), offset_x, offset_y);
+ }
return;
case GSK_COLOR_MATRIX_NODE:
break; /* Fallback */
}
- {
- GdkTexture *texture;
- guint32 texture_id;
- FallbackCacheElement *hit;
- float t_off_x = 0, t_off_y = 0;
-
- hit = g_hash_table_lookup (self->fallback_cache, node);
- if (hit)
- {
- texture = g_object_ref (hit->texture);
- t_off_x = hit->off_x;
- t_off_y = hit->off_y;
- hit->used_in_frame = self->frame_nr;
- }
- else
- {
- FallbackCacheElement *element;
-
- texture = node_texture_fallback (node, &t_off_x, &t_off_y);
-#if 0
- g_print ("Fallback %p for %s\n", texture, node->node_class->type_name);
-#endif
- element = g_new0 (FallbackCacheElement, 1);
- element->texture = g_object_ref (texture);
- element->node = gsk_render_node_ref (node);
- element->off_x = t_off_x;
- element->off_y = t_off_y;
- element->used_in_frame = self->frame_nr;
- g_hash_table_insert (self->fallback_cache, element->node, element);
- }
-
- g_ptr_array_add (node_textures, texture); /* Transfers ownership to node_textures */
- texture_id = gdk_broadway_display_ensure_texture (display, texture);
- add_uint32 (nodes, BROADWAY_NODE_TEXTURE);
- add_float (nodes, node->bounds.origin.x + t_off_x - offset_x);
- add_float (nodes, node->bounds.origin.y + t_off_y - offset_y);
- add_float (nodes, gdk_texture_get_width (texture));
- add_float (nodes, gdk_texture_get_height (texture));
- add_uint32 (nodes, texture_id);
- }
-}
-
-static gboolean
-clean_old_fallbacks (gpointer key,
- gpointer value,
- gpointer user_data)
-{
- GskBroadwayRenderer *self = GSK_BROADWAY_RENDERER (user_data);
- FallbackCacheElement *element = value;
-
- /* Remove cached fallbacks not used for 5 frames */
- return self->frame_nr - element->used_in_frame > 5;
+ if (add_new_node (renderer, node, BROADWAY_NODE_TEXTURE))
+ {
+ GdkTexture *texture;
+ cairo_surface_t *surface;
+ cairo_t *cr;
+ guint32 texture_id;
+ int x = floorf (node->bounds.origin.x);
+ int y = floorf (node->bounds.origin.y);
+ int width = ceil (node->bounds.origin.x + node->bounds.size.width) - x;
+ int height = ceil (node->bounds.origin.y + node->bounds.size.height) - y;
+
+ surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height);
+ cr = cairo_create (surface);
+ cairo_translate (cr, -x, -y);
+ gsk_render_node_draw (node, cr);
+ cairo_destroy (cr);
+
+ texture = gdk_texture_new_for_surface (surface);
+ g_ptr_array_add (self->node_textures, texture); /* Transfers ownership to node_textures */
+
+ texture_id = gdk_broadway_display_ensure_texture (display, texture);
+ add_float (nodes, x - offset_x);
+ add_float (nodes, y - offset_y);
+ add_float (nodes, gdk_texture_get_width (texture));
+ add_float (nodes, gdk_texture_get_height (texture));
+ add_uint32 (nodes, texture_id);
+ }
}
static void
{
GskBroadwayRenderer *self = GSK_BROADWAY_RENDERER (renderer);
- self->frame_nr++;
+ self->node_lookup = g_hash_table_new (g_direct_hash, g_direct_equal);
gdk_draw_context_begin_frame (GDK_DRAW_CONTEXT (self->draw_context), update_area);
- gsk_broadway_renderer_add_node (renderer, self->draw_context->nodes, self->draw_context->node_textures, root, 0, 0);
+
+ /* These are owned by the draw context between begin and end, but
+ cache them here for easier access during the render */
+ self->nodes = self->draw_context->nodes;
+ self->node_textures = self->draw_context->node_textures;
+
+ gsk_broadway_renderer_add_node (renderer, root, 0, 0);
+
+ self->nodes = NULL;
+ self->node_textures = NULL;
+
gdk_draw_context_end_frame (GDK_DRAW_CONTEXT (self->draw_context));
- g_hash_table_foreach_remove (self->fallback_cache, clean_old_fallbacks, renderer);
+ if (self->last_node_lookup)
+ g_hash_table_unref (self->last_node_lookup);
+ self->last_node_lookup = self->node_lookup;
+ self->node_lookup = NULL;
+
+ if (self->last_root)
+ gsk_render_node_unref (self->last_root);
+ self->last_root = gsk_render_node_ref (root);
+
+ if (self->next_node_id > G_MAXUINT32 / 2)
+ {
+ /* We're "near" a wrap of the ids, lets avoid reusing any of
+ * these nodes next frame, then we can reset the id counter
+ * without risk of any old nodes sticking around and conflicting. */
+
+ g_hash_table_remove_all (self->last_node_lookup);
+ self->next_node_id = 0;
+ }
}
static void
static void
gsk_broadway_renderer_init (GskBroadwayRenderer *self)
{
- self->fallback_cache = g_hash_table_new_full ((GHashFunc)g_direct_hash,
- (GEqualFunc)g_direct_equal,
- NULL,
- (GDestroyNotify)fallback_cache_element_free);
}